package com.lancoo.cvas.arrangecourse.dataprocess;


import com.lancoo.cvas.arrangecourse.common.BinaryUtil;
import com.lancoo.cvas.arrangecourse.common.ClassHourUtil;
import com.lancoo.cvas.arrangecourse.entities.ClassHour;
import com.lancoo.cvas.arrangecourse.entities.TimeslotRoom;
import com.lancoo.cvas.pojo.dto.ArrangePeriod;
import com.lancoo.cvas.pojo.dto.ArrangeRoomDto;
import com.lancoo.cvas.pojo.dto.ArrangeRoomRuleDto;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Description 生成教室课时分组
 * @Author Fortysun
 * @Date 2023-03-27 10:25
 */
@Slf4j
public class GenerateTimeslotRoom {

    public GenerateTimeslotRoom() {

    }

    /***
     * 初始化数据
     * @param roomList 排课的教室信息
     * @param timeslotList 排课的课时信息，过滤掉了禁排的课时
     * @param prohibitTimeslotRoomList 禁排的教室课时信息；
     * @param classHourMap 锁定的排课单元信息
     * @param weekNum 学期总周数
     */
    public LinkedList<TimeslotRoom> getResult(List<ArrangeRoomDto> roomList,
                                              List<ArrangePeriod> timeslotList,
                                              List<ArrangeRoomRuleDto> prohibitTimeslotRoomList,
                                              ConcurrentHashMap<String, CopyOnWriteArrayList<ClassHour>> classHourMap,
                                              Long weekNum) {
        //用于设置，timeslotRoomId的起始编号
        Integer size = ClassHourUtil.getMapElementSize(classHourMap);
        //合并所有的教室和课时信息到分组
        LinkedList<TimeslotRoom> allTimeslotRoomList = mergeTimeslotRoom(roomList, timeslotList, weekNum, size);
        //排除掉禁排的教室课时分组信息；
        allTimeslotRoomList = removeProhibitTimeslotRoom(allTimeslotRoomList, prohibitTimeslotRoomList);
        //排除掉锁定的教室课时分组信息；
        allTimeslotRoomList = removeLockedTimeslotRoom(allTimeslotRoomList, classHourMap);

        return allTimeslotRoomList;
    }

    /**
     * 合并教室和课时信息到timeslotRoom列表
     * @param roomList 教室列表
     * @param timeslotList 课时列表
     * @param weekNum 学期总周数
     * @param startPos 索引从多少开始
     * @return
     */
    private LinkedList<TimeslotRoom> mergeTimeslotRoom(List<ArrangeRoomDto> roomList,
                                                 List<ArrangePeriod> timeslotList,
                                                       Long weekNum,
                                                       Integer startPos) {
        if (roomList == null || roomList.isEmpty()
                || timeslotList == null || timeslotList.isEmpty()) {
            log.info("GenerateTimeslotRoom--mergeTimeslotRoom: 教室或课时列表为空，无法生成教室课时分组信息列表~！！");
            return new LinkedList<>();
        }

        //存储全部合并的教室课时分组信息；
        LinkedList<TimeslotRoom> timeslotRoomList = new LinkedList<>();

        int roomIdIndex = 0;
        int timeslotSize = timeslotList.size();
        for (ArrangeRoomDto room : roomList) {
            int timeslotIdIndex = 0;
            for (ArrangePeriod timeslot : timeslotList) {
                //非禁排的课时，过滤掉禁排的课时
                if (timeslot.getIsProhibit() == 0) {
                    TimeslotRoom timeslotRoom = new TimeslotRoom();
                    timeslotRoom.setTimeslotRoomId(startPos + roomIdIndex*timeslotSize + timeslotIdIndex+1);
                    timeslotRoom.setTimeslotCode(timeslot.getTimeCode());
                    timeslotRoom.setTimeslotName(timeslot.getCodeName());
                    timeslotRoom.setPeriodType(timeslot.getPeriodType());
                    timeslotRoom.setPeriod(timeslot.getPeriod());
                    //日期编号，周一01，周二02...依次类推
                    timeslotRoom.setDayNo(timeslot.getTimeCode().substring(0, 2));
                    timeslotRoom.setRoomId(room.getRoomId());
                    timeslotRoom.setRoomName(room.getRoomName());
                    timeslotRoom.setRoomType(room.getRoomType());
                    timeslotRoom.setWeeks(initWeeks(weekNum));
                    timeslotRoom.setCapacity(room.getRoomNum());
                    timeslotRoom.setBuildingId(room.getBuildingId());
                    timeslotRoom.setBuildingName(room.getBuildingName());
                    timeslotRoom.setCampusId(room.getCampusId());
                    timeslotIdIndex++;
                    timeslotRoomList.add(timeslotRoom);
                }
            }
            roomIdIndex++;
        }

        return timeslotRoomList;
    }

    /**
     * 根据教学班的周次字符串的长度，生成周次二进制字符串；
     * @param weekNum 学期总周数
     * @return
     */
    private String initWeeks(Long weekNum) {
        //如果为空，需要抛异常出去
        if (weekNum==null || weekNum==0L) {
            try {
                throw new Exception("#GenerateTimeslotRoom.initWeeks# 教学班周次字符串为空~！！");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        String weeks = "";
        for (int i = 0; i < weekNum; i++) {
            weeks += "1";
        }

        return weeks;
    }

    /**
     * 从生成好的分组中，去掉需要排除的教室课时分组信息
     * @param allTimeslotRoomList 所有合并后的教室课时分组信息
     * @param removeimeslotRoomList 需要排除的教室课时分组信息；
     * @return
     */
    private LinkedList<TimeslotRoom> removeProhibitTimeslotRoom(LinkedList<TimeslotRoom> allTimeslotRoomList,
                                                           List<ArrangeRoomRuleDto> removeimeslotRoomList) {
        if (removeimeslotRoomList != null && !removeimeslotRoomList.isEmpty()
        && allTimeslotRoomList != null && !allTimeslotRoomList.isEmpty()) {
            for (ArrangeRoomRuleDto removeTimeslotRoom : removeimeslotRoomList) {
                //定义列表遍历的迭代器
                Iterator iterator = allTimeslotRoomList.iterator();
                while (iterator.hasNext()) {
                    TimeslotRoom allTimeslotRoom = (TimeslotRoom) iterator.next();
                    //判断教室信息是否相等，以及课时编码是否相等
                    if (Objects.equals(allTimeslotRoom.getRoomId(), removeTimeslotRoom.getRoomId())
                            && allTimeslotRoom.getTimeslotCode().equals(removeTimeslotRoom.getTimeCode())) {
                        //从合并后的教室课时分组中，去掉禁排的教室课时分组信息；
                        iterator.remove();;
                        break;
                    }
                }

            }
        }

        return allTimeslotRoomList;
    }

    /**
     * 从生成好的分组中，去掉已锁定教学班的教室课时分组信息
     * @param allTimeslotRoomList 所有合并后的教室课时分组信息
     * @param classHourMap 锁定的排课单元信息
     * @return
     */
    private LinkedList<TimeslotRoom> removeLockedTimeslotRoom(LinkedList<TimeslotRoom> allTimeslotRoomList,
                                      ConcurrentHashMap<String, CopyOnWriteArrayList<ClassHour>> classHourMap) {
        if (classHourMap != null && !classHourMap.isEmpty()
        && allTimeslotRoomList != null && !allTimeslotRoomList.isEmpty()) {
            //遍历map
            for (Map.Entry<String, CopyOnWriteArrayList<ClassHour>> entry : classHourMap.entrySet()) {
                //处理map对应的课表信息列表数据
                allTimeslotRoomList = removeTimeslotCodeData(allTimeslotRoomList, entry.getValue());
            }
        }

        return allTimeslotRoomList;
    }

    /**
     *
     * @param allTimeslotRoomList 全部可分配的教室课时分组信息
     * @param classHourList 指定课时，对应的课表数据
     * @return
     */
    private LinkedList<TimeslotRoom> removeTimeslotCodeData(LinkedList<TimeslotRoom> allTimeslotRoomList,
                                                            List<ClassHour> classHourList) {
        if (classHourList != null && !classHourList.isEmpty()) {
            for (ClassHour classHour : classHourList) {
                //获取课表对应，分配的教室课时分组信息列表对象
                TimeslotRoom timeslotRoom= classHour.getTimeslotRoom();
                //将课表里面的教室课时分组信息列表对象，从所有的待分配的教室课时分组信息对象中剔除；
                allTimeslotRoomList = DealClassHourTimeslotRoomList(allTimeslotRoomList, timeslotRoom);
            }
        }

        return allTimeslotRoomList;
    }

    /**
     * 处理课表对象，分配的教室课时分组信息列表
     * @param allTimeslotRoomList 所有的教室课时分组信息
     * @param timestlotRoom 课表对象的，教室课时分组信息；
     * @return
     */
    private LinkedList<TimeslotRoom> DealClassHourTimeslotRoomList(LinkedList<TimeslotRoom> allTimeslotRoomList,
                                                                   TimeslotRoom timestlotRoom) {

        //定义列表遍历的迭代器
        Iterator iterator = allTimeslotRoomList.iterator();
        while (iterator.hasNext()) {
            TimeslotRoom allTimeslotRoom = (TimeslotRoom) iterator.next();
            //判断教室课时分组信息是否相等
            if (timestlotRoom!=null
                    && timestlotRoom.equals(allTimeslotRoom)) {
                String teachingClassWeeks = timestlotRoom.getWeeks();
                String timeslotRoomWeeks = allTimeslotRoom.getWeeks();

                //如果周次完全匹配上面
                if (teachingClassWeeks.equals(timeslotRoomWeeks)) {
                    //从合并后的教室课时分组中，去掉禁排的教室课时分组信息；
                    iterator.remove();
                } else {//部分匹配上
                    //将两个二进制字符串，进行与操作,比如10101和11111，进行与操作，结果是10101
                    String result = BinaryUtil.binaryAnd(timeslotRoomWeeks, teachingClassWeeks);
                    //如果结果字符串和教学班的周次相等，说明当前教室课时分组的周次，是可以分配给当前教学班的
                    //同时确定，跟已经分配好的课时，是否存在学生或者教师冲突的情况
                    if (teachingClassWeeks.equals(result)) {
                        //将两个二进制字符串，进行异或运算，比如10101和11111
                        //结果就是01010，这个值，就是剩下的可排课的周次
                        result = BinaryUtil.binaryXOR(timeslotRoomWeeks, teachingClassWeeks);
                        //此时只需要将当前教室课时分组的周次信息，更新为01010即可，
                        //供后面的教学班使用
                        allTimeslotRoom.setWeeks(result);
                    }
                }
                break;
            }
        }

        return allTimeslotRoomList;
    }
}
